// Copyright (C) 2012 Yury Bychenkov (http://bychenkov.com).
// All rights reserved.
//
// Redistribution and use in source and binary forms, with or without
// modification, are permitted provided that the following conditions are
// met:
//
//  - Redistributions of source code must retain the above copyright
// notice, this list of conditions and the following disclaimer.
//  - Redistributions in binary form must reproduce the above
// copyright notice, this list of conditions and the following disclaimer
// in the documentation and/or other materials provided with the
// distribution.
//  - Neither the name of Yury Bychenkov nor the names of its
// contributors may be used to endorse or promote products derived from
// this software without specific prior written permission.
//
// THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
// "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
// LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
// A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
// OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
// SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
// LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
// DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
// THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
// (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
// OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

#ifndef query_h
#define query_h

#include "common.h"
#include "schema.h"
#include "numberutil.h"
#include "resourcebundle.h"
#include "traits.h"

struct ASTNode {
	enum ASTNodeType {
		QUERY,

		SELECT,
		TOP,
		WHERE,
		ORDER_BY,

		ASC,
		DESC,

   	   	STRING_VALUE,
   	   	INTEGER_VALUE,
   	   	DECIMAL_VALUE,
    
   	   	STRING_ID,
   	   	INTEGER_ID,
   	   	DECIMAL_ID,
    
		AND,
		OR,
    
		EQ,
		LE,
		LT,
		GE,
		GT,
		NE 
	};

	ASTNode(uint type, const void* data = 0, uint size = 0);
	ASTNode(uint type, ASTNode* child1);
	ASTNode(uint type, ASTNode* child1, ASTNode* child2);
	~ASTNode();
	void print(int level = 0);

	uint type;
	vector<ASTNode*> children;
	vector<uint8> data;
};

enum ByteCode {
	BC_RET_TRUE,	// return true
	BC_RET_FALSE,   // return false

	BC_IF,			// relative jump if true

	BC_EQ_I,		// indexOf(column) = indexOf(value)
	BC_LE_I,        // indexOf(column) <= indexOf(value)
	BC_LT_I,        // indexOf(column) < indexOf(value)
	BC_GE_I,        // indexOf(column) >= indexOf(value)
	BC_GT_I,        // indexOf(column) > indexOf(value)
	BC_NE_I,        // indexOf(column) <> indexOf(value)

	BC_EQ_I_S,      // stringValue(column) = value
	BC_LE_I_S,      // stringValue(column) <= value
	BC_LT_I_S,      // stringValue(column) < value
	BC_GE_I_S,      // stringValue(column) >= value
	BC_GT_I_S,      // stringValue(column) > value
	BC_NE_I_S,      // stringValue(column) <> value

	BC_EQ_I_L,      // integerValue(column) = value 
	BC_LE_I_L,      // integerValue(column) <= value
	BC_LT_I_L,      // integerValue(column) < value 
	BC_GE_I_L,      // integerValue(column) >= value
	BC_GT_I_L,      // integerValue(column) > value 
	BC_NE_I_L,      // integerValue(column) <> value

	BC_EQ_I_D,      // decimalValue(column) = value 
	BC_LE_I_D,      // decimalValue(column) <= value
	BC_LT_I_D,      // decimalValue(column) < value 
	BC_GE_I_D,      // decimalValue(column) >= value
	BC_GT_I_D,      // decimalValue(column) > value 
	BC_NE_I_D       // decimalValue(column) <> value
};


class Query {
public:
	~Query();

	bool hasOrdering() const;
	uint getTopCount() const;
	vector<uint> getTupleColumns() const;
	
private:
	struct Projection {
		uint columnIndex;
	};

	struct Order {
		uint columnIndex;
		bool asc;
	};

private:
	Query(const Schema& schema, const uint16* code, uint codeCount, uint topCount, const vector<Projection>& projections, const vector<Order>& ordering, const ResourceBundle& data_);

	const Schema& schema;
	uint16* code;
	const uint codeCount;

	uint topCount;
	vector<Projection> projections;
	vector<Order> ordering;

	ResourceBundle data;

	friend class QueryCompiler;
	template <typename Provider> friend class QueryExecutor;
};

class QueryCompiler {
public:
	static const uint DEFAULT_TOP_COUNT = 50;
	static const uint MAX_TOP_COUNT = 1024 * 1024;

	static Query* compile(const Schema& schema, const char* queryString);

	void fail(const char* message);
	void success(ASTNode* node);

private:
    QueryCompiler(const Schema& schema);
    ~QueryCompiler();

	Query* compile(const char* queryString);
	bool compile(const ASTNode* node, uint falseJoint, uint trueJoint);

	bool parse(const char* s);

	uint addRef(const ASTNode* node);

	void begin();
	void push(int token, const void* value = 0);
	void end();

private:
	const Schema& schema;

	ASTNode* root;
	void* parser;	
	bool failed;
	string message;

	vector<uint16> code;
	ResourceBundle bundle;

	static Logger logger;
};

#include "query.hpp"

#endif // query_h
